home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockPhotobucketService.js < prev    next >
Text File  |  2007-10-12  |  74KB  |  2,178 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16. //
  17.  
  18. const ENABLE_DEBUG = true;
  19. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockPhotobucketService: "+x+"\n"); }
  20.  
  21. const Cc = Components.classes;
  22. const Ci = Components.interfaces;
  23. const Cr = Components.results;
  24. const Cu = Components.utils;
  25.  
  26. const PHOTOBUCKET_TITLE             = "PhotoBucket Web Service";
  27. const PHOTOBUCKET_FAVICON           = "http://www.photobucket.com/favicon.ico";
  28. const PHOTOBUCKET_CID               = Components.ID('{d3b147b0-a321-11da-a746-0800200c9a66}');
  29. const PHOTOBUCKET_CONTRACTID        = "@flock.com/?photo-api-photobucket;1";
  30. const SERVICE_ENABLED_PREF          = "flock.service.photobucket.enabled";
  31. const CATEGORY_COMPONENT_NAME       = "Photobucket JS Component"
  32. const CATEGORY_ENTRY_NAME           = "photobucket"
  33.  
  34. const flockIError                   = Components.interfaces.flockIError;
  35. const flockIPhoto                   = Components.interfaces.flockIPhoto;
  36. const flockIPhotoAccount            = Components.interfaces.flockIPhotoAccount;
  37. const flockIPhotoAlbum              = Components.interfaces.flockIPhotoAlbum;
  38. const flockIPhotoAPI                = Components.interfaces.flockIPhotoAPI;
  39. const flockIPhotoPerson             = Components.interfaces.flockIPhotoPerson;
  40. const flockIPasswordOrigin          = Components.interfaces.flockIPasswordOrigin;
  41.  
  42. const FLOCK_PHOTO_ALBUM_CONTRACTID  = "@flock.com/photo-album;1";
  43. const FLOCK_PHOTO_CONTRACTID        = "@flock.com/photo;1";
  44. const FLOCK_PHOTOPERSON_CONTRACTID  = "@flock.com/photo-person;1";
  45. const FLOCK_PHOTO_ACCOUNT_CONTRACTID = "@flock.com/photo-account;1";
  46. const FLOCK_ERROR_CONTRACTID        = "@flock.com/error;1";
  47.  
  48. const PHOTOBUCKET_API_VERSION       = "1.0";
  49. const PHOTOBUCKET_TOKEN_PREF        = "flock.photo.photobucket.token";
  50. const PHOTOBUCKET_SESSION_REFRESH_INTERVAL = 21600000; // every 6 hours refresh the session token
  51.  
  52. const PHOTOBUCKET_PK                = "e57335b0847da8b1105b6c8bc27d217a";
  53. const PHOTOBUCKET_SCID              = "149825788";
  54.  
  55. // services
  56. const RDFS = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
  57. const IOS = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  58. const RDFCU = Cc["@mozilla.org/rdf/container-utils;1"].getService(Ci.nsIRDFContainerUtils);
  59.  
  60. var gCompTK;
  61. function getCompTK() {
  62.   if (!gCompTK) {
  63.     gCompTK = Cc["@flock.com/singleton;1"]
  64.                         .getService(Ci.flockISingleton)
  65.                         .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  66.                         .wrappedJSObject;
  67.   }
  68.   return gCompTK;
  69. }
  70.  
  71. // for Cardinal to Danphe migration
  72. const FLOCK_NS = "http://flock.com/rdf#";
  73. const PEOPLE_PHOTO_RDF_ROOT = "urn:flock:people:photo:lists:watched";
  74.  
  75. // String defaults... may be updated later through Web Detective
  76. var gStrings = {
  77.   "domains": "photobucket.com",
  78.   "userloginURL": "http://photobucket.com/login.php",
  79.   "userprofileURL": "http://photobucket.com/images/search/%accountid%",
  80.   "serviceloginURL": "http://photobucket.com/svc/servicelogin.php",
  81.   "apiURL": "http://photobucket.com/svc/api.php"
  82. };
  83.  
  84.  
  85. // ====================================================
  86. // ========== BEGIN General Helper Functions ==========
  87. // ====================================================
  88.  
  89. function loadSubScript(spec)
  90. {
  91.   var loader = Components.classes['@mozilla.org/moz/jssubscript-loader;1']
  92.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  93.   var context = {};
  94.   loader.loadSubScript(spec, context);
  95.   return context;
  96. }
  97.  
  98. function loadLibraryFromSpec(aSpec) {
  99.   var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  100.                          .getService(Components.interfaces.mozIJSSubScriptLoader);
  101.   loader.loadSubScript(aSpec);
  102. }
  103.  
  104. loadLibraryFromSpec("chrome://browser/content/flock/common/flocksafe.js");
  105. loadLibraryFromSpec("chrome://browser/content/flock/common/md5.js");
  106. loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
  107.  
  108.  
  109. function photobucketPhoto() {
  110. }
  111.  
  112. photobucketPhoto.prototype= {
  113.   id: "",
  114.   thumbnail: "",
  115.   webPageUrl: "",
  116.   midSizePhoto: "",
  117.   largeSizePhoto: "",
  118.   title: "",
  119.   username: "",
  120.   userid: "",
  121.   is_video: "",
  122.   svcShortName: 'photobucket',
  123.  
  124.   buildTooltip: function( ) {
  125.     // do we have to use document from the window to ceate elements? -- ja
  126.     var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  127.                        .getService(Components.interfaces.nsIWindowMediator);
  128.     var win = wm.getMostRecentWindow('navigator:browser');
  129.     if (!win) return null;
  130.  
  131.     var hbox = win.document.createElement('hbox');
  132.     var box = win.document.createElement('vbox');
  133.     box.setAttribute('style', 'max-width: 250px');
  134.     var title = win.document.createElement('label');
  135.     title.setAttribute('value', this.title );
  136.     title.setAttribute('crop', 'end');
  137.     box.appendChild(title);
  138.     var lbl = win.document.createElement('label');
  139.     lbl.setAttribute('value', this.username );
  140.     lbl.setAttribute('class', 'user');
  141.     box.appendChild(lbl);
  142.     hbox.appendChild(box)
  143.  
  144.     var vbox = win.document.createElement('vbox');
  145.     var cbox = win.document.createElement('hbox');
  146.     var largeImg = win.document.createElement('image');
  147.     largeImg.setAttribute('src', this.midSizePhoto);
  148.     largeImg.setAttribute('style', 'margin-bottom: 2px;');
  149.     var spacer = win.document.createElement('spacer'); 
  150.     spacer.setAttribute('flex', '1');
  151.     cbox.appendChild(largeImg);
  152.     cbox.appendChild(spacer);
  153.     vbox.appendChild(cbox);
  154.     vbox.appendChild(hbox);
  155.  
  156.     return vbox;
  157.   },
  158.   buildHTML: function( ) {
  159.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.midSizePhoto+'" border="0" /></a>';
  160.   },
  161.   buildLargeHTML: function( ) {
  162.     return '<a title="'+this.title+'" href="'+this.webPageUrl+'"><img src="'+this.largeSizePhoto+'" border="0" /></a>';
  163.   },
  164.   buildBBCode: function ( ) {
  165.     if(this.is_video)
  166.     {
  167.       return '[photob]' + this.largeSizePhoto + '[photob]'
  168.       
  169.     }else
  170.     {
  171.       return '[url=' + this.webPageUrl + '][img]'+ this.largeSizePhoto +'[/img][/url]';
  172.     }
  173.   },
  174.   buildMiniPage: function ( ) {
  175.     return '<html><head><title>' + this.title + ' (' + this.username + ')</title></head>' +
  176.            '<body><object width="425" height="350">' +
  177.            '<param name="movie" value="'+this.largeSizePhoto+'"/>' +
  178.            '<center><embed src="'+this.largeSizePhoto+'&autoplay=1" type="application/x-shockwave-flash" width="425" height="350"/></object></center></body></html>';
  179.   },
  180.   QueryInterface: function(iid) {
  181.     if (!iid.equals(Components.interfaces.nsISupports) &&
  182.         !iid.equals(Components.interfaces.flockIPhoto)) {
  183.       throw Components.results.NS_ERROR_NO_INTERFACE;
  184.     }
  185.     return this;
  186.   }
  187. };
  188.  
  189.  
  190. // ====================================================
  191. // ========== BEGIN photobucketService class ==========
  192. // ====================================================
  193.  
  194. function photobucketService()
  195. {
  196.   this.state = flockIPhotoAPI.LOGGED_OUT;
  197.   this.obs = Components.classes["@mozilla.org/observer-service;1"]
  198.                        .getService(Components.interfaces.nsIObserverService);
  199.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  200.                            .getService(Components.interfaces.flockIAccountUtils);
  201.   this.url = "http://www.photobucket.com";
  202.   this.initialized = false;
  203.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  204.   this.logger.init("photobucket");
  205.   this._ctk = {
  206.     interfaces: [
  207.       "nsISupports",
  208.       "nsIClassInfo",
  209.       "nsISupportsCString",
  210.       "nsIObserver",
  211.       "flockIPhotoAPI",
  212.       "flockIPollingService",
  213.       //"flockIPeopleActionController",
  214.       "flockIWebService",
  215.       "flockIAuthenticateNewAccount",
  216.       "flockIManageableWebService",
  217.       "flockIMediaWebService",
  218.       "flockISocialWebService"
  219.     ],
  220.     shortName: "photobucket",
  221.     fullName: "Photobucket",
  222.     description: PHOTOBUCKET_TITLE,
  223.     favicon: PHOTOBUCKET_FAVICON,
  224.     contractID: PHOTOBUCKET_CONTRACTID,
  225.     accountClass: photobucketAccount,
  226.     needPassword: false
  227.   };
  228.  
  229.   this.init();
  230. }
  231.  
  232.  
  233. // BEGIN nsIObserver interface
  234. photobucketService.prototype.observe =
  235. function photobucketService_observe(aSubject, aTopic, aData)
  236. {
  237. }
  238. // END nsIObserver interface
  239.  
  240. var channels = {
  241.   'special:recent': {
  242.     title: 'Recent Public Photos',
  243.     supportsSearch: false
  244.   }
  245. }
  246.  
  247. photobucketService.prototype.getChannel =
  248. function photobucketService_getChannel(aChannelId)
  249. {
  250.   if (!(aChannelId in channels)) return null;
  251.  
  252.   var nc = Components.classes['@flock.com/media-channel;1']
  253.                      .createInstance(Components.interfaces.flockIMediaChannel);
  254.   nc.id = aChannelId;
  255.   nc.title = channels[aChannelId].title
  256.   nc.supportsSearch = channels[aChannelId].supportsSearch;
  257.   return nc;
  258. }
  259.  
  260. photobucketService.prototype.__defineGetter__('channels', function () {
  261.   var ar = new Array();
  262.  
  263.   for (var id in channels) {
  264.     var nc = Components.classes['@flock.com/media-channel;1']
  265.                        .createInstance(Components.interfaces.flockIMediaChannel);
  266.     nc.id = id;
  267.     nc.title = channels[id].title
  268.     nc.supportsSearch = channels[id].supportsSearch;
  269.     ar.push(nc);
  270.   }
  271.  
  272.   var rval = {
  273.     getNext: function() {
  274.       var rval = ar.shift();
  275.       return rval;
  276.     },
  277.     hasMoreElements: function() {
  278.       return (ar.length > 0);
  279.     }
  280.   }
  281.   return rval;
  282. })
  283.  
  284.  
  285.  
  286. photobucketService.prototype.shortName = "photobucket";
  287. photobucketService.prototype.serviceName = "Photobucket";
  288.  
  289. photobucketService.prototype.getPhotoFromRDFNode =
  290. function (aRDFId)
  291. {
  292.   var newPhoto = new photobucketPhoto();
  293.   var coopPhoto = this.faves_coop.get(aRDFId);
  294.   newPhoto.webPageUrl = coopPhoto.URL;
  295.   newPhoto.thumbnail = coopPhoto.thumbnail;
  296.   newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
  297.   newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
  298.   newPhoto.username = coopPhoto.username;
  299.   newPhoto.userid = coopPhoto.userid;
  300.   newPhoto.title = coopPhoto.name;
  301.   newPhoto.id = coopPhoto.photoid;
  302.   newPhoto.icon = coopPhoto.favicon;
  303.   newPhoto.uploadDate = coopPhoto.datevalue;
  304.   newPhoto.is_public = coopPhoto.is_public;
  305.   newPhoto.is_video = coopPhoto.is_video;
  306.   return newPhoto;
  307. }
  308.  
  309. photobucketService.prototype.handlePhotosResult =
  310. function photobucketService_handlePhotosResult(aXML) {
  311.   var inst = this;
  312.   function createItem(media, is_video) {
  313.     var uploadDate = media.getAttribute("uploaddate");
  314.     var title = media.getAttribute("name");
  315.     var username = media.getAttribute("username");
  316.     var page_url = "";
  317.     var thumb_url = media.getElementsByTagName('thumb')[0].firstChild.nodeValue;
  318.     var small_url = media.getElementsByTagName('url')[0].firstChild.nodeValue;
  319.     // this is a photobucket api issue - need a fix -
  320.     try {
  321.       page_url = media.getElementsByTagName('browseurl')[0].firstChild.nodeValue;
  322.       page_url = page_url.replace('&', '&');
  323.     }
  324.     catch(e) {
  325.       page_url = small_url;
  326.     }
  327.     var newPhoto = new photobucketPhoto();
  328.     newPhoto.webPageUrl = page_url;
  329.     newPhoto.thumbnail = thumb_url;
  330.     newPhoto.midSizePhoto = thumb_url;
  331.     newPhoto.largeSizePhoto = small_url;
  332.     newPhoto.title = title;
  333.     newPhoto.username = username;
  334.     newPhoto.userid = username;
  335.     if (uploadDate) {
  336.       newPhoto.id = uploadDate;
  337.       newPhoto.uploadDate = parseInt(uploadDate)*1000;
  338.     } else {
  339.       newPhoto.id = username + ":" + title;
  340.     }
  341.     newPhoto.is_video = is_video;
  342.     newPhoto.has_miniView = is_video;
  343.     return newPhoto;
  344.   }
  345.  
  346.   try {
  347.     var rval = [];
  348.  
  349.     var videoList = aXML.getElementsByTagName("video");
  350.     for (var i = 0; i < videoList.length; i++) {
  351.       rval.push(createItem(videoList[i], true));
  352.     }
  353.  
  354.     var photoList = aXML.getElementsByTagName("photo");
  355.     for (var i = 0; i < photoList.length; i++) {
  356.       rval.push(createItem(photoList[i], false));
  357.     }
  358.   } catch(e) {
  359.     inst.logger.error(e);
  360.   }
  361.  
  362.   // sort the photos and videos together by date
  363.   // the follow sorts based on ID which we are using the uploadDate to be the id
  364.   // it is ugly -- and probably not as efficient as it could be --- but it
  365.   // is only sorting 20 items at most so it is good enough for now - ja
  366.   rval.sort(function(a,b) {
  367.     return (b.id-a.id);
  368.   });
  369.  
  370.   return rval;
  371. }
  372.  
  373. photobucketService.prototype.getContacts =
  374. function photobucketService_getContacts(aListener)
  375. {
  376.   return -1;  // throw unsupported exception
  377. }
  378.  
  379. photobucketService.prototype.getValidPerson =
  380. function photobucketService_getValidPerson(aListener, aURL)
  381. {
  382.   var user = aURL.match(/albums\/[^\/]*\/([^\/]*)\//)[1];
  383.   var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID].createInstance(flockIPhotoPerson);
  384.   newUserObj.id = user
  385.   newUserObj.username = user;
  386.   newUserObj.fullname = user;
  387.   newUserObj.service = this;
  388.   aListener.onGetValidPerson(newUserObj);
  389. }
  390.  
  391. photobucketService.prototype.addCoopPerson =
  392. function photobucketService_addCoopPerson(aPhotoPerson)
  393. {
  394. }
  395.  
  396. photobucketService.prototype.findByUsername =
  397. function photobucketService_findByUsername(aListener, aUsername)
  398. {
  399.   var inst = this;
  400.   var myListener = {
  401.     onResult: function (aXML) {
  402.       //var user = aXML.getElementsByTagName("photo")[0];
  403.       // We should be getting all the user's details from the response xml, but
  404.       // the pbucket response has all pertinent info with the photo tag.  If
  405.       // there are no photos, then we have no user info
  406.       var newUserObj = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
  407.                                  .createInstance(flockIPhotoPerson);
  408.       newUserObj.id = aUsername;
  409.       newUserObj.username = aUsername;
  410.       newUserObj.fullname = aUsername;
  411.       newUserObj.service = inst;
  412.       aListener.onFindByUsernameResult(newUserObj);
  413.     },
  414.     onError: function (aXML) {
  415.       aListener.onError(aXML);
  416.     }
  417.   }
  418.   var params = {};
  419.   params.username = aUsername;
  420.   var dict = params2Dictionary(params);
  421.   this.authenticatedCall(myListener, "getrecentusermedia", dict);
  422. }
  423.  
  424. function MultiGetter() {
  425. }
  426.  
  427. MultiGetter.prototype = {
  428.   mTimer: Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer),
  429.   init: function(aSvc, aListener, aEnumerator) {
  430.     this.mHasNew = false;
  431.     this.mNeedSave = false;
  432.     this.mSvc = aSvc;
  433.     this.mListener = aListener;
  434.     this.mArray = new Array();
  435.     while (aEnumerator.hasMoreElements()) {
  436.       var p = aEnumerator.getNext();
  437.       p.QueryInterface(Components.interfaces.flockIPhotoPerson);
  438.  
  439.       this.mArray.push(p);
  440.     }
  441.     this.next();
  442.   },
  443.   finish: function() {
  444.     //dump("FINITI\n");
  445.     if (this.mHasNew) {
  446.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_HAS_NEW);
  447.     }
  448.     else if (this.mNeedSave) {
  449.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NEED_SAVE);
  450.     }
  451.     else {
  452.       this.mListener.onGetMostRecentPhotoForList(Components.interfaces.flockIPhotoAPIListener.LIST_NO_CHANGE);
  453.     }
  454.     return;
  455.   },
  456.   updatePerson: function(aPerson, aPhoto) {
  457.     //dump(aPerson.username + "updating person\n");
  458.     var seq = aPerson.seq;
  459.     var newSeq = parseInt(aPhoto.id);
  460.     if (newSeq > seq) {
  461.       var oldHasNew = aPerson.hasNew;
  462.       aPerson.seq = aPhoto.id;
  463.       this.mNeedSave = true;
  464.       if (seq != 0) {
  465.         this.mHasNew = true;
  466.         aPerson.hasNew = true
  467.         if (oldHasNew == false) {
  468.           aPerson.lastNewSeq = seq + 1;
  469.         }
  470.       }
  471.     }
  472.   },
  473.   notify: function() {
  474.     this.doNext();
  475.   },
  476.   next: function() {
  477.     this.mTimer.initWithCallback(this, 1000, 0);
  478.   },
  479.   doNext: function() {
  480.     //dump("NEXT PLEASE\n");
  481.     var inst = this;
  482.  
  483.     if (!this.mArray.length) {
  484.       this.finish();
  485.       return;
  486.     }
  487.     var person = this.mArray.pop();
  488.  
  489.     var listener = {
  490.       onSearchResult: function(aEnumerator) {
  491.       //dump("b search result\n");
  492.         while (aEnumerator.hasMoreElements()) {
  493.           var photo = aEnumerator.getNext();
  494.           inst.updatePerson(person, photo);
  495.           break;
  496.         }
  497.         inst.next();
  498.       },
  499.       onError: function(aError) {
  500.       //dump("b search eror\n");
  501.         inst.finish();
  502.       }
  503.     }
  504.     this.mSvc.search(listener, person.id, "", "", 1, 1);
  505.   }
  506. }
  507.  
  508. photobucketService.prototype.getMostRecentPhotoForList =
  509. function photobucketService_getMostRecentPhotoForList(aListener, aEnumerator)
  510. {
  511.   var mg = new MultiGetter();
  512.   mg.init(this, aListener, aEnumerator);
  513. }
  514.  
  515. photobucketService.prototype.supportsSearch =
  516. function photobucketService_supportsSearch( aQueryString ) {
  517.  
  518.   var aQuery = new queryHelper(aQueryString);
  519.  
  520.   if (aQuery.special) {
  521.     var channel = channels["special:" + aQuery.special];
  522.     if (channel) {
  523.       return channel.supportsSearch;
  524.     }
  525.   }
  526.  
  527.   if (aQuery.user)
  528.     return true;
  529.   if (aQuery.search)
  530.     return false;
  531.   return null;
  532. }
  533.  
  534.  
  535. photobucketService.prototype.queryChannel =
  536. function photobucketService_queryChannel(aListener, aQueryString, aCount, aPage)
  537. {
  538.   var aQuery = new queryHelper(aQueryString);
  539.   var inst = this;
  540.  
  541.   var myListener = {
  542.     onResult: function (aXML) {
  543.       var rval = inst.handlePhotosResult(aXML);
  544.       var enum_ = {
  545.         hasMoreElements: function() {
  546.           return (rval.length > 0);
  547.         },
  548.         getNext: function() {
  549.           return rval.shift();
  550.         }
  551.       }
  552.       aListener.onSearchResult(enum_);
  553.     },
  554.     onError: function (aError) {
  555.       // if we get an error here this probably means user inputted an illegal character
  556.       // do nothign and let the "Sorry, no results found" message appear
  557.     }
  558.   }
  559.  
  560.   var params = {
  561.     page: aPage,
  562.     num: aCount
  563.   }
  564.  
  565.   if (aQuery.getKey('special') == 'recent') {
  566.     var dict = params2Dictionary(params);
  567.     this.call(myListener, "getrecentphotos", dict);
  568.   }
  569.  
  570.   if (aQuery.hasKey('search')) {
  571.     // don't pass in encoded text for PB
  572.     // if an illegal char is used, we will return no results
  573.     var aText = aQuery.search;
  574.     if (aText && aText.length) {
  575.       params.query = aText.split(".")[0];
  576.       var dict = params2Dictionary(params);
  577.       this.call(myListener, "search", dict);
  578.     }
  579.   }
  580. }
  581.  
  582. photobucketService.prototype.search =
  583. function photobucketService_search(aListener, aQueryString, aCount, aPage)
  584. {
  585.   var aQuery = new queryHelper(aQueryString);
  586.   if (!aQuery.user) {
  587.        this.queryChannel(aListener, aQueryString, aCount, aPage);
  588.        return;
  589.   }
  590.   var aUserid = aQuery.user;
  591.  
  592.   var inst = this;
  593.  
  594.   var myListener = {
  595.     onResult: function (aXML) {
  596.       var rval = inst.handlePhotosResult(aXML);
  597.       var enum_ = {
  598.         hasMoreElements: function() {
  599.           return (rval.length > 0);
  600.         },
  601.         getNext: function() {
  602.           return rval.shift();
  603.         }
  604.       }
  605.       aListener.onSearchResult(enum_);
  606.     },
  607.     onError: function (aError) {
  608.       aListener.onError(aError);
  609.     }
  610.   }
  611.  
  612.   var params = {};
  613.  
  614.  
  615.   if (aUserid) {
  616.     params.username = aUserid;
  617.     params.perpage = aCount;
  618.   }
  619.  
  620.   if (aQuery.search) params.query = aQuery.getEncodedKey('search').split(".")[0];
  621.   if (aPage) params.page = aPage;
  622.   if (aQuery.album) params.album_name = aQuery.album;
  623.  
  624.   params.media = 'all';
  625.  
  626.   var dict = params2Dictionary(params);
  627.   if (params.query != null) {
  628.     this.call(myListener, "search", dict);
  629.   } else if (params.username != null) {
  630.     if (params.album_name) {
  631.       this.authenticatedCall(myListener, "getuseralbumpaginated", dict);
  632.     }else
  633.       this.authenticatedCall(myListener, "getrecentusermedia", dict);
  634.   }
  635.  
  636. }
  637.  
  638. photobucketService.prototype.createAlbum =
  639. function photobucketService_createAlbum(aListener, aPath)
  640. {
  641.   var svc = this;
  642.   this.logger.debug("createAlbum - aPath = "+aPath);
  643.   var albumCreationListener = {
  644.     onResult: function(aXML) {
  645.       svc.logger.info(aPath + " album successfully created");
  646.       var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID]
  647.                                .createInstance(flockIPhotoAlbum);
  648.       var newAlbumPath;
  649.       if (params.parent_album_name) {
  650.         newAlbumPath = params.parent_album_name + params.new_album_name;
  651.       } else {
  652.         newAlbumPath = params.new_album_name;
  653.       }
  654.       newAlbum.title = newAlbumPath;
  655.       newAlbum.id = newAlbumPath;
  656.       aListener.onCreateAlbum(newAlbum);
  657.     },
  658.     onError: function(aError) {
  659.       svc.logger.error("Error in album creation");
  660.       aListener.onError(aError);
  661.     }
  662.   }
  663.  
  664.   var parentDir = "";
  665.   var pathName = aPath.split('/');
  666.   var params = new Object();
  667.   params.new_album_name = pathName.pop();
  668.   var dict;
  669.  
  670.   for (var i = 0; i < pathName.length; i++) {
  671.     parentDir += pathName[i] + '/';
  672.   }
  673.  
  674.   if (parentDir.length) {
  675.     params.parent_album_name = parentDir;
  676.   }
  677.  
  678.   dict = params2Dictionary(params);
  679.   this.authenticatedCall(albumCreationListener, "createalbum", dict);
  680. }
  681.  
  682. photobucketService.prototype.handleAlbumsResult =
  683. function photobucketService_handleAlbumsResult(aXML)
  684. {
  685.   var rval = new Array();
  686.   var albumList = aXML.getElementsByTagName("photoalbum");
  687.   for (var i = 0; i < albumList.length; i++) {
  688.     var album = albumList[i];
  689.     var title = album.getAttribute("name");
  690.  
  691.     // the first album node has no name attr, so skip it
  692.     if (!title) continue;
  693.  
  694.     var username = album.getAttribute('username');
  695.     var id;
  696.     // parse out the prefix /username/
  697.     if (title.match(album.getAttribute('username')) && title != username) {
  698.       id = title.substring(username.length + 1);
  699.       var newAlbum = Components.classes[FLOCK_PHOTO_ALBUM_CONTRACTID]
  700.                                .createInstance(flockIPhotoAlbum);
  701.       newAlbum.id = id;
  702.       newAlbum.title = id;
  703.       rval.push(newAlbum);
  704.     }
  705.   }
  706.   return rval;
  707. }
  708.  
  709. photobucketService.prototype.getAccountStatus =
  710. function photobucketService_getAccountStatus(aListener)
  711. {
  712.   var inst = this;
  713.   var myListener = {
  714.     onResult: function(aXML) {
  715.       try {
  716.         var photoAccount = Components.classes[FLOCK_PHOTO_ACCOUNT_CONTRACTID].createInstance(flockIPhotoAccount);
  717.         photoAccount.username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;;
  718.         photoAccount.maxSpace = aXML.getElementsByTagName('megabytes_allowed')[0].firstChild.nodeValue;
  719.         photoAccount.usedSpace = aXML.getElementsByTagName('megabytes_used')[0].firstChild.nodeValue;
  720.         var isPremium = aXML.getElementsByTagName('premium')[0].firstChild.nodeValue;
  721.         photoAccount.maxFileSize = '';
  722.         photoAccount.usageUnits = "megabytes";
  723.         photoAccount.isPremium = (isPremium == "1" ? true:false);
  724.         aListener.onGetAccountStatus(photoAccount);
  725.       } catch (ex) {
  726.         inst.logger.error('getaccountstatus error :' + ex);
  727.       }
  728.     },
  729.     onError: function (aError) {
  730.       aListener.onError(aError);
  731.     }
  732.   }
  733.   var params = new Object();
  734.   var dict = params2Dictionary(params);
  735.   this.authenticatedCall(myListener, "getuserinfo", dict);
  736. }
  737.  
  738. photobucketService.prototype.getAlbums =
  739. function photobucketService_getAlbums(aListener, aUsername)
  740. {
  741.   var inst = this;
  742.   var myListener = {
  743.     onResult: function (aXML) {
  744.       try {
  745.         var rval = inst.handleAlbumsResult(aXML);
  746.         var enum_ = {
  747.           hasMoreElements: function() {
  748.             return (rval.length > 0);
  749.           },
  750.           getNext: function() {
  751.             return rval.shift();
  752.           }
  753.         }
  754.         aListener.onGetAlbumsResult(enum_);
  755.       } catch(e) {
  756.         inst.logger.error(e);
  757.         aListener.onError(null);
  758.       }
  759.     },
  760.     onError: function (aXML) {
  761.       aListener.onError(aXML);
  762.     }
  763.   }
  764.   var params = new Object();
  765.   params.recurse = "true";
  766.   params.view = "flat"
  767.   params.media = "none";
  768.   if (aUsername) {
  769.     params.username = aUsername;
  770.   }
  771.   var dict = params2Dictionary(params);
  772.   this.authenticatedCall(myListener, "getuseralbum", dict);
  773. }
  774.  
  775. photobucketService.prototype.state = flockIPhotoAPI.LOGGED_OUT;
  776. photobucketService.prototype.mAuthUser = null;
  777. photobucketService.prototype.mUploader = null;
  778. photobucketService.prototype.mApiUrl = gStrings["apiURL"];
  779. photobucketService.prototype.iconUrl = "chrome://browser/skin/flock/photo/photobucketIcon.png";
  780.  
  781. /* features for service */
  782. photobucketService.prototype.supportsFeature =
  783. function photobucketService_supportsFeature(aFeature)
  784. {
  785.   var supports = {};
  786.   supports.tags = false;
  787.   supports.title = false;
  788.   supports.contacts = false;
  789.   supports.privacy = false;
  790.   supports.fileName = true;
  791.   supports.albumCreation = true;
  792.   return (supports[aFeature] == true);
  793. }
  794.  
  795. photobucketService.prototype.mSupportsTitle = false;
  796. photobucketService.prototype.mSupportsTags = false;
  797. photobucketService.prototype.mSupportsContacts = false;
  798. photobucketService.prototype.mSupportsPrivacey = false;
  799. photobucketService.prototype.mSessionRefreshTimer = null;
  800.  
  801. photobucketService.prototype.init =
  802. function photobucketService_init()
  803. {
  804.   if (this.initialized) return;
  805.   this.initialized = true;
  806.  
  807.   DEBUG(".init()");
  808.  
  809.   this.prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
  810.   if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  811.        !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
  812.   {
  813.     DEBUG("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
  814.     var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  815.     catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
  816.     catMgr.deleteCategoryEntry("flockIPhotoAPI", CATEGORY_ENTRY_NAME, true);
  817.     catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
  818.     return;
  819.   }
  820.  
  821.   this.mAuthUser = {};
  822.  
  823.   this.faves_coop = Components.classes['@flock.com/singleton;1'].getService(Components.interfaces.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject;
  824.  
  825.   this.urn = 'urn:photobucket:service';
  826.   this.pbService = new this.faves_coop.Service(
  827.     this.urn,
  828.     {
  829.       name: "photobucket",
  830.       desc: "The Photobucket Service",
  831.       contactLabel: 'People'
  832.     }
  833.   );
  834.   this.pbService.serviceId = PHOTOBUCKET_CONTRACTID;
  835.  
  836.   // Load Web Detective file
  837.   this.webDetective = this.acUtils.useWebDetective("photobucket.xml");
  838.   for (var s in gStrings) {
  839.     gStrings[s] = this.webDetective.getString("photobucket", s, gStrings[s]);
  840.   }
  841.   this.mApiUrl = gStrings["apiURL"];
  842.   this.pbService.domains = gStrings["domains"];
  843.   this.pbService.loginURL = gStrings["userloginURL"];
  844.  
  845.   /*
  846.   var pbHomepage = new this.faves_coop.Favorite(
  847.     "urn:photobucket:actions:pbhomepage", {
  848.       name: "PhotoBucket.com",
  849.       URL: "http://www.PhotoBucket.com",
  850.     });
  851.   this.pbService.children.add(pbHomepage);
  852.   */
  853.  
  854.   var pbOpenProfile = new this.faves_coop.Action(
  855.     "urn:photobucket:actions:openprofile", {
  856.       name: "Open Profile",
  857.       method: "openProfile",
  858.       service: PHOTOBUCKET_CONTRACTID,
  859.       flavour: "view"
  860.     });
  861.   this.pbService.children.addOnce(pbOpenProfile);
  862.  
  863.   var pbViewPhotos = new this.faves_coop.Action(
  864.     "urn:photobucket:actions:viewPhotos", {
  865.       name: 'View Photos',
  866.       method: 'viewPhotos',
  867.       service: PHOTOBUCKET_CONTRACTID,
  868.       flavour: 'view'
  869.     });
  870.   this.pbService.children.addOnce(pbViewPhotos);
  871.  
  872.   var pbUploadPhotos = new this.faves_coop.Action(
  873.   "urn:photobucket:actions:uploadPhotos", {
  874.     name: 'Upload Photo',
  875.     method: 'uploadPhotos',
  876.     isPrimary: true,
  877.     service: PHOTOBUCKET_CONTRACTID,
  878.     flavour: 'accountaction'
  879.   });
  880.   this.pbService.children.addOnce(pbUploadPhotos);
  881.  
  882.   this.photoPersonRoot = new this.faves_coop.Folder(
  883.     "urn:photobucket:peopleroot",
  884.     {name: "Photobucket People" }
  885.   );
  886.  
  887.   // Update auth states
  888.   try {
  889.     if (this.webDetective.detectCookies("photobucket", "loggedout", null)) {
  890.       this.acUtils.markAllAccountsAsLoggedOut(PHOTOBUCKET_CONTRACTID);
  891.       this.state = flockIPhotoAPI.LOGGED_OUT;
  892.       this.mAuthUser = null;
  893.       this.mUploader = null;
  894.     } else {
  895.       this.checkPersistedToken();
  896.     }
  897.   } catch (ex) {
  898.     this.logger.error("ERROR updating auth states for Photobucket: "+ex);
  899.   }
  900. }
  901.  
  902. photobucketService.prototype.doAction =
  903. function photobucketService_doAction(aActionName, aURN, aSubject) {
  904.   switch (aActionName) {
  905.     case "uploadPhotos":
  906.         var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  907.                               .getService(Components.interfaces.nsIWindowMediator);
  908.         var win = wm.getMostRecentWindow('navigator:browser');
  909.  
  910.         if (win) {
  911.           win.flock_photo.util.launchUploader();
  912.         }
  913.         break;
  914.     default:
  915.       break;
  916.   }
  917. }
  918.  
  919. photobucketService.prototype.checkPersistedToken =
  920. function photobucketService_checkPersistedToken()
  921. {
  922.   var acctURN = this.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  923.   if (!acctURN) { return; }
  924.   var token = this.faves_coop.get(acctURN).authToken;
  925.   if (token) {
  926.     this.logger.debug('checking if the session associated with the token is still valid ... ' + token);
  927.     // try to get the session from the persisted token
  928.     var inst = this;
  929.     var myListener = {
  930.       onResult: function(aXML) {
  931.         try {
  932.           var resp = aXML.getElementsByTagName('response');
  933.           if (resp[0].getAttribute('stat') == 'ok') {
  934.             var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  935.             var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
  936.             inst.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
  937.             inst.getAuthUser().id = username;
  938.             inst.getAuthUser().username = username;
  939.             inst.getAuthUser().fullname = username;
  940.             inst.getAuthUser().sessionkey = sessionkey;
  941.             inst.state = flockIPhotoAPI.LOGGED_IN;
  942.             if (!inst.mSessionRefreshTimer) inst.fireSessionRefreshTimer();
  943.           } else {
  944.             inst.state = flockIPhotoAPI.LOGGED_OUT;
  945.             inst.mAuthUser = null;
  946.             this.mUploader = null;
  947.           }
  948.         }
  949.         catch(e) {
  950.           inst.logger.error(e);
  951.         }
  952.       },
  953.       onError: function(aErr) {
  954.         return false;
  955.       }
  956.     }
  957.     var params = {};
  958.     params.service_token = token;
  959.     inst.call(myListener, "getsession", params2Dictionary(params));
  960.   }
  961.   //this.mAuthUser.sessionkey = null;
  962. }
  963.  
  964. photobucketService.prototype.getAuthState =
  965. function photobucketService_getAuthState()
  966. {
  967.   return this.state;
  968. }
  969.  
  970. photobucketService.prototype.__defineGetter__("supportsTitle", function () { return this.mSupportsTitle; })
  971. photobucketService.prototype.__defineGetter__("supportsTags", function () { return this.mSupportsTags; })
  972. photobucketService.prototype.__defineGetter__("supportsContacts", function () { return this.mSupportsContacts; })
  973. photobucketService.prototype.__defineGetter__("supportsPrivacey", function () { return this.mSupportsPrivacey; })
  974. photobucketService.prototype.__defineGetter__("authState", function () { return this.state; })
  975.  
  976. photobucketService.prototype.logout =
  977. function photobucketService_logout()
  978. {
  979.   this.state = flockIPhotoAPI.LOGGED_OUT;
  980.   this.mAuthUser = null;
  981.   this.mUploader = null;
  982.   if (this.mSessionRefreshTimer) { this.mSessionRefreshTimer.cancel(); }
  983.   this.acUtils.removeCookies(this.webDetective.getSessionCookies("photobucket"));
  984. }
  985.  
  986. photobucketService.prototype.getToken =
  987. function photobucketService_getToken(aListener)
  988. {
  989.   var inst = this;
  990.   var tokenListener = {
  991.     onResult: function (aXML) {
  992.       try {
  993.         var resp = aXML.getElementsByTagName("response");
  994.         if (resp[0].getAttribute("stat") == "ok") {
  995.           //rval = resp[0].firstChild.nodeValue;
  996.           aListener.onResult(aXML);
  997.         } else {
  998.           aListener.onError(null);  //TODO - figure out errors
  999.           //rval = -1;
  1000.         }
  1001.       }
  1002.       catch(e) {
  1003.         inst.logger.error(e);
  1004.       }
  1005.     },
  1006.     onError: function (aError) {
  1007.       inst.logger.error(aError.errorString);
  1008.       aListener.onError(aError);
  1009.     }
  1010.   }
  1011.   var params = {};
  1012.   this.call(tokenListener, "getservicetoken", params2Dictionary(params));
  1013. }
  1014.  
  1015. photobucketService.prototype.login =
  1016. function photobucketService_login(aAccountURN, aListener)
  1017. {
  1018.   this.logger.debug(".login('"+aAccountURN+"')");
  1019.   var inst = this;
  1020.   if (this.state == flockIPhotoAPI.LOGGING_IN) return;
  1021.   this.state = flockIPhotoAPI.LOGGING_IN;
  1022.  
  1023.   var timer = Components.classes["@mozilla.org/timer;1"]
  1024.                         .createInstance(Components.interfaces.nsITimer);
  1025.   var inst = this;
  1026.   timerFunc = {
  1027.     notify: function(aTimer) {
  1028.       inst.doLogin(aAccountURN, aListener);
  1029.     }
  1030.   }
  1031.   timer.initWithCallback(timerFunc, 0, 0);  //re-check token
  1032. }
  1033.  
  1034. photobucketService.prototype._isLoggedIn =
  1035. function (aListener)
  1036. {
  1037.   if (this.mAuthUser == null) return null;
  1038.   return (this.getAuthUser().sessionkey != null);
  1039. }
  1040.  
  1041. photobucketService.prototype.__defineGetter__('isLoggedIn', function () { return this._isLoggedIn(); })
  1042.  
  1043. photobucketService.prototype.dictionary2Params =
  1044. function (aDictionary)
  1045. {
  1046.   var params = {};
  1047.   var obj = {};
  1048.   var count = {};
  1049.   var keys = aDictionary.getKeys(count, obj);
  1050.   for (var i = 0; i < keys.length; ++i) {
  1051.     var supports = aDictionary.getValue(keys[i]);
  1052.     var supportsString = supports.QueryInterface(Components.interfaces.nsISupportsString);
  1053.     var val = supportsString.toString();
  1054.     params[keys[i]] = val;
  1055.   }
  1056.   return params;
  1057. }
  1058.  
  1059. photobucketService.prototype.getAuthUser =
  1060. function photobucketService_getAuthUser()
  1061. {
  1062.   return this.mAuthUser;
  1063. }
  1064.  
  1065. photobucketService.prototype.getAuthPerson =
  1066. function photobucketService_getAuthPerson()
  1067. {
  1068.   var user = this.mAuthUser;
  1069.   var person = {};
  1070.   person.id = user.id;
  1071.   person.fullname = user.fullname;
  1072.   person.username = user.username;
  1073.   person.service = this;
  1074.   return person;
  1075. }
  1076.  
  1077. //ASY - this should be implemented
  1078. photobucketService.prototype._isUploading =
  1079. function photobucketService__isUploading()
  1080. {
  1081.   return false;
  1082. }
  1083.  
  1084. photobucketService.prototype.__defineGetter__(
  1085.   'isUploading',
  1086.   function photobucketService_isUploading() { return this._isUploading(); }
  1087. )
  1088.  
  1089. photobucketService.prototype.doCall =
  1090. function photobucketService_doCall(aListener, aMethod, aDictionary, aAuth)
  1091. {
  1092.   var inst = this;
  1093.   var params = this.dictionary2Params(aDictionary);
  1094.   var url = this.buildUrl(inst.mApiUrl, aMethod, params, aAuth);
  1095.   //dump(url+" < call\n");
  1096.   this.logger.debug(url + " < call");
  1097.   this.req = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  1098.                        .createInstance(Components.interfaces.nsIXMLHttpRequest)
  1099.                        .QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  1100.   
  1101.   this.req.open('GET', url, true);
  1102.   var req = this.req;
  1103.   this.req.onreadystatechange = function (aEvt) {
  1104.     if (req.readyState == 4) {
  1105.       try {
  1106.         inst.logger.debug("photobucketService: doCall[onreadystatechange] req.status = "+req.status);
  1107.         if (req.status/100 == 2) {
  1108.           inst.logger.debug(req.responseText);
  1109.           var rsp = req.responseXML.getElementsByTagName("response")[0];
  1110.           var stat = rsp.getAttribute("stat");
  1111.           if (stat != "ok") {
  1112.             aListener.onError(inst.getError("SERVICE_ERROR", req.responseXML, null));
  1113.           } else {
  1114.             aListener.onResult(req.responseXML);
  1115.           }
  1116.         } else {
  1117.           aListener.onError(inst.getError("HTTP_ERROR", null, req.status));
  1118.         }
  1119.       } catch(e) {
  1120.         inst.logger.error(e);
  1121.         // beware of constant
  1122.         aListener.onError(inst.getError("HTTP_ERROR", null, "9001"));
  1123.       }
  1124.     }
  1125.   }
  1126.   this.req.send(null);
  1127. }
  1128.  
  1129. photobucketService.prototype.call =
  1130. function photobucketService_call(aListener, aMethod, aDictionary, aAuth)
  1131. {
  1132.   return this.doCall(aListener, aMethod, aDictionary, aAuth);
  1133. }
  1134.  
  1135. photobucketService.prototype.authenticatedCall =
  1136. function photobucketService_authenticatedCall(aListener, aMethod, aDictionary)
  1137. {
  1138.   this.logger.debug('authenticatedCall ' + aMethod);
  1139.   if (!this.isLoggedIn) {
  1140.     this.logger.debug('not logged in, so trying unauth call for ' + aMethod);
  1141.     return this.call(aListener, aMethod, aDictionary);
  1142.   }
  1143.   this.logger.debug('+++++' + this.getAuthUser().sessionkey);
  1144.   return this.call(aListener, aMethod, aDictionary, true);
  1145. }
  1146.  
  1147. photobucketService.prototype.buildUrl =
  1148. function photobucketService_buildUrl(aUrl, aMethod, aParams, aAuth)
  1149. {
  1150.   var qs = "";
  1151.   var strConcat = "";
  1152.   for (var p in aParams) {
  1153.     qs += "&" + escape(p) + "=" + escape(aParams[p]);
  1154.     strConcat += escape(aParams[p]);
  1155.   }
  1156.   var rval = aUrl
  1157.       + "?method=" + aMethod
  1158.       + "&version=" + PHOTOBUCKET_API_VERSION;
  1159.   if (aAuth) {
  1160.     rval += "&session_key=" + this.getAuthUser().sessionkey;
  1161.   }
  1162.   rval += "&scid=" + PHOTOBUCKET_SCID
  1163.       + qs
  1164.       + "&sig=" + this.buildSig(aMethod, aParams, aAuth);
  1165.   this.logger.debug("this is your buildUrl: " + rval + "\n");
  1166.   return rval;
  1167. }
  1168.  
  1169. photobucketService.prototype.buildSig =
  1170. function photobucketService_buildSig(aMethod, aParams, aAuth)
  1171. {
  1172.   var qs = "";
  1173.   var strConcat = "";
  1174.   for (var p in aParams) {
  1175.     qs += "&" + escape(p) + "=" + escape(aParams[p]);
  1176.     if (p == 'service_token') {
  1177.       strConcat += escape(aParams[p]);
  1178.     }
  1179.   }
  1180.  
  1181.   this.logger.debug(strConcat + "<<<<<<<<strconcat\n");
  1182.   var preHash = aMethod + PHOTOBUCKET_PK + PHOTOBUCKET_SCID + strConcat + (aAuth?this.getAuthUser().sessionkey:"");
  1183.   this.logger.debug('going to sig::: ' + preHash);
  1184.  
  1185.   var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
  1186.                             .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  1187.   converter.charset = "UTF-8";
  1188.  
  1189.   var inputStream = converter.convertToInputStream(preHash);
  1190.   return hex_md5_stream(inputStream);
  1191. }
  1192.  
  1193. photobucketService.prototype.notify = function photobucketService_sessionTimerNotify(aTimer) {
  1194.   var inst = this;
  1195.   var sessionRefreshListener = {
  1196.     onAuth: function() {
  1197.       inst.logger.debug('yay! pbucket refreshed its session key with the timer');
  1198.     },
  1199.  
  1200.     onError: function(aError) {
  1201.       inst.logger.error('!!!boo! error refreshing session key on the timer');
  1202.     }
  1203.   }
  1204.   this.getSessionKey(sessionRefreshListener);
  1205. }
  1206.  
  1207.  
  1208. photobucketService.prototype.fireSessionRefreshTimer = function photobucketService_fireSessionRefreshTimer() {
  1209.   this.mSessionRefreshTimer = Components.classes['@mozilla.org/timer;1'].createInstance(Components.interfaces.nsITimer);
  1210.   // refresh the session key once an hour, probably can adjust this
  1211.   this.mSessionRefreshTimer.initWithCallback(this, PHOTOBUCKET_SESSION_REFRESH_INTERVAL, 2);
  1212. }
  1213.  
  1214.  
  1215. photobucketService.prototype.doLogin =
  1216. function photobucketService_doLogin(aAccountURN, aListener)
  1217. {
  1218.   this.logger.debug(".doLogin('"+aAccountURN+"')");
  1219.   var inst = this;
  1220.   var tokenListener = {
  1221.     onResult: function(aXML) {
  1222.       inst.logger.debug(".doLogin('"+aAccountURN+"'): tokenListener: onResult()");
  1223.       // Time to authenticate...
  1224.       var params = {
  1225.         service_token: aXML.getElementsByTagName("service_token")[0].firstChild.nodeValue
  1226.       };
  1227.       // persist the token
  1228.       var acctCoopObj = inst.faves_coop.get(aAccountURN);
  1229.       acctCoopObj.authToken = params.service_token;
  1230.  
  1231.       // listener for the upcoming "getsession" api call
  1232.       var sessionListener = {
  1233.         onResult: function(aXML) {
  1234.           inst.logger.debug(".doLogin('"+aAccountURN+"'): tokenListener: sessionListener: onResult()");
  1235.           var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  1236.           var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
  1237.           inst.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
  1238.           inst.getAuthUser().id = username;
  1239.           inst.getAuthUser().username = username;
  1240.           inst.getAuthUser().fullname = username;
  1241.           inst.getAuthUser().sessionkey = sessionkey;
  1242.           inst.state = flockIPhotoAPI.LOGGED_IN;
  1243.           inst.acUtils.ensureOnlyAuthenticatedAccount(aAccountURN);
  1244.           if (!inst.mSessionRefreshTimer) inst.fireSessionRefreshTimer();
  1245.           aListener.onAuth();
  1246.         },
  1247.         onError: function(aErr) {
  1248.           inst.logger.error(".doLogin('"+aAccountURN+"'): tokenListener: sessionListener: onError()");
  1249.           inst.logger.error('>>>>>> ' + aErr.errorString);
  1250.           inst.state = flockIPhotoAPI.LOGGED_OUT;
  1251.           aListener.onError(aErr);
  1252.         }
  1253.       };
  1254.  
  1255.       // If we have the user's un/pw, then we can do this silently...
  1256.       var pw = inst.acUtils.getPassword(inst.urn+":"+acctCoopObj.accountId);
  1257.       if (pw) {
  1258.         inst.logger.debug(".doLogin('"+aAccountURN+"'): tokenListener: onResult(): found password");
  1259.  
  1260.         var postBody = "action=login"
  1261.                      + "&service_token="+params.service_token
  1262.                      + "&sig="+inst.buildSig("login", params)
  1263.                      + "&scid="+PHOTOBUCKET_SCID
  1264.                      + "&method=login"
  1265.                      + "&callback_verify="
  1266.                      + "&username="+pw.user
  1267.                      + "&password="+pw.password
  1268.                      + "&login=Login";
  1269.         var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  1270.           .createInstance(Components.interfaces.nsIXMLHttpRequest)
  1271.           .QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  1272.  
  1273.         var onReadyStateFunc = function(eEvt) {
  1274.           if (hr.readyState == 4) {
  1275.             inst.logger.debug(".doLogin('"+aAccountURN+"'): tokenListener: onResult(): hr.onreadystatechange()");
  1276.             var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1277.                                     .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1278.             if (inst.webDetective.detectNoDOM("photobucket", "apiauth", '', hr.responseText, results)) {
  1279.               var hr2 = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  1280.                 .createInstance(Components.interfaces.nsIXMLHttpRequest)
  1281.                 .QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  1282.  
  1283.               hr2.onreadystatechange = function(eEvt2) {
  1284.                 if (hr2.readyState == 4) {
  1285.                   inst.call(sessionListener, "getsession", params2Dictionary(params));
  1286.                 }
  1287.               }
  1288.  
  1289.               var postBody2 = "action=complete"
  1290.                            + "&authorized=yes"
  1291.                            + "&service_token="+params.service_token
  1292.                            + "&sig="+inst.buildSig("login", params)
  1293.                            + "&scid="+PHOTOBUCKET_SCID
  1294.                            + "&method=login"
  1295.                            + "&callback_verify="
  1296.                            + "&user_id=" + results.getPropertyAsAString("user_id");
  1297.  
  1298.               hr2.detachLoadGroup = true;
  1299.               hr2.open("POST", gStrings["serviceloginURL"],true);
  1300.               hr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  1301.               hr2.send(postBody2);
  1302.             } else {
  1303.               inst.call(sessionListener, "getsession", params2Dictionary(params));
  1304.             }
  1305.           }
  1306.         };
  1307.  
  1308.         hr.onreadystatechange = onReadyStateFunc;
  1309.         hr.detachLoadGroup = true;
  1310.         hr.open("POST", gStrings["serviceloginURL"],true);
  1311.         hr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  1312.         hr.send(postBody);
  1313.  
  1314.       } else {
  1315.         inst.logger.debug(".doLogin('"+aAccountURN+"'): tokenListener: onResult(): NO password!");
  1316.  
  1317.         // No password stored, so we have to prompt the user
  1318.         var windowManager = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService();
  1319.         var windowManagerInterface = windowManager.QueryInterface( Components.interfaces.nsIWindowMediator);
  1320.         var topWindow = windowManagerInterface.getMostRecentWindow(null);
  1321.  
  1322.         var loginUrl = inst.buildUrl(gStrings["serviceloginURL"], "login", params);
  1323.         var url = "chrome://browser/content/flock/photo/photoLoginWindow.xul?"
  1324.                 + "url="+escape(loginUrl)
  1325.                 + "&finalString="+escape("You have granted");
  1326.         topWindow.open( url,
  1327.                         "_blank",
  1328.                         "chrome,close,titlebar,resizable=yes,toolbar,dialog=no,scrollbars=yes,modal,centerscreen" );
  1329.         // we are waiting till window closes
  1330.  
  1331.         inst.call(sessionListener, "getsession", params2Dictionary(params));
  1332.       }
  1333.     },
  1334.     onError: function(aErr) {
  1335.       inst.logger.error(".doLogin('"+aAccountURN+"'): tokenListener: onError()");
  1336.       inst.state = flockIPhotoAPI.LOGGED_OUT;
  1337.       aListener.onError(aErr);
  1338.     }
  1339.   }
  1340.  
  1341.   if (!this.mAuthUser) {
  1342.     this.mAuthUser = {};
  1343.   }
  1344.   this.getToken(tokenListener);
  1345. }
  1346.  
  1347. photobucketService.prototype.getSessionKey =
  1348. function photobucketService_getSessionKey(aListener)
  1349. {
  1350.   var acctURN = this.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  1351.   if (!acctURN) aListener.onError(null);
  1352.   var token = this.faves_coop.get(acctURN).token;
  1353.   this.logger.debug(">>>getSessionKey called with the token "+token+"\n\n\n");
  1354.  
  1355.   var inst = this;
  1356.   var mySessionListener = {
  1357.     onResult: function(aXML) {
  1358.       var username = aXML.getElementsByTagName('username')[0].firstChild.nodeValue;
  1359.       var sessionkey = aXML.getElementsByTagName('session_key')[0].firstChild.nodeValue;
  1360.       inst.mApiUrl = aXML.getElementsByTagName('url')[0].firstChild.nodeValue;
  1361.       inst.getAuthUser().id = username;
  1362.       inst.getAuthUser().username = username;
  1363.       inst.getAuthUser().fullname = username;
  1364.       inst.getAuthUser().sessionkey = sessionkey;
  1365.       inst.state = flockIPhotoAPI.LOGGED_IN;
  1366.       aListener.onAuth();
  1367.     },
  1368.     onError: function(aErr) {
  1369.       inst.logger.debug('>>>>>> ' + aErr.errorString + '\n\n')
  1370.       inst.state = flockIPhotoAPI.LOGGED_OUT;
  1371.       aListener.onError(aErr);
  1372.     }
  1373.   };
  1374.   var params = {};
  1375.   params.service_token = token;
  1376.   inst.call(mySessionListener, "getsession", params2Dictionary(params));
  1377. }
  1378.  
  1379. photobucketService.prototype.upload2 =
  1380. function photobucketService_upload2(aListener, aUpload, aFile)
  1381. {
  1382.   var inst = this;
  1383.   if (!this.isLoggedIn) throw "Error - assertion failure, should be loggedin";
  1384.   var sigParams = {};
  1385.   var myParams = {};
  1386.  
  1387.   this.logger.debug('uploading to album ' + aUpload.album);
  1388.   myParams.version = PHOTOBUCKET_API_VERSION;
  1389.   myParams.scid = PHOTOBUCKET_SCID;
  1390.   myParams.method = 'upload';
  1391.   myParams.description = aUpload.description;
  1392.   myParams.title = aUpload.title;
  1393.   myParams.album_name = aUpload.album;
  1394.   myParams.session_key = this.getAuthUser().sessionkey;
  1395.   myParams.sig = this.buildSig('upload', sigParams, true);
  1396.  
  1397.   sigParams.session_key = this.getAuthUser().sessionkey;
  1398.  
  1399.   this.mUploader = new PhotoUploader();
  1400.   this.mUploader.setEndpoint(inst.mApiUrl);
  1401.   this.mUploader.photoParam = "uploadfile";
  1402.  
  1403.   var myListener = {
  1404.     onResult: function(aXML) {
  1405.       var photo = aXML.getElementsByTagName("photo")[0];
  1406.       if (photo) {
  1407.         inst.logger.debug('uploaded the pic named ' + photo.getAttribute('name'));
  1408.         var rval = inst.handlePhotosResult(aXML);
  1409.         aListener.onUploadComplete(aUpload);
  1410.         aListener.onUploadFinalized(aUpload, rval[0]);
  1411.       }
  1412.       else {
  1413.         aListener.onError(inst.getError('SERVICE_ERROR', aXML, null));
  1414.       }
  1415.     },
  1416.     onError: function(aErrorCode) {
  1417.       if (aErrorCode) {
  1418.         aListener.onError(inst.getError('HTTP_ERROR', null, aErrorCode));
  1419.       } else {
  1420.         aListener.onError(inst.getError('SERVICE_ERROR', null, null));
  1421.       }
  1422.     },
  1423.     onProgress: function(aCurrentProgress) {
  1424.         aListener.onProgress(aCurrentProgress);
  1425.     }
  1426.   }
  1427.   this.mUploader.upload(myListener, aFile, myParams, aUpload);
  1428. }
  1429.  
  1430. photobucketService.prototype.getError =
  1431. function photobucketService_getError(aErrorType, aXML, aHTTPErrorCode)
  1432. {
  1433.   var errorNode;
  1434.   var code;
  1435.   var serverErrorMessage;
  1436.   var error = Components.classes[FLOCK_ERROR_CONTRACTID].createInstance(flockIError);
  1437.  
  1438.   this.logger.debug("getError aErrorType="+aErrorType+"; aHTTPErrorCode="+aHTTPErrorCode);
  1439.  
  1440.   if (aErrorType == "HTTP_ERROR") {
  1441.     error.errorCode = aHTTPErrorCode;
  1442.     return error;
  1443.   } else if (aErrorType == "SERVICE_ERROR") {
  1444.     try {
  1445.       errorNode = aXML.getElementsByTagName("error")[0];
  1446.       code = errorNode.getAttribute('code');
  1447.       serverErrorMessage = errorNode.getAttribute('msg');
  1448.       error.serviceErrorCode = code;
  1449.       error.serviceErrorString = serverErrorMessage;
  1450.       this.logger.debug("Found error code = "+code + " and message = '" + serverErrorMessage+"'");
  1451.     } catch (ex) {
  1452.       code = "-1"
  1453.     }
  1454.  
  1455.     if (!code) code = "-1";
  1456.  
  1457.     switch (code) {
  1458.       case "-1":
  1459.         error.errorCode = error.PHOTOSERVICE_INVALID_RESPONSE;
  1460.         break;
  1461.  
  1462.       case "007":
  1463.         error.errorCode = error.PHOTOSERVICE_LOGIN_FAILED;
  1464.         break;
  1465.  
  1466.       case "008":
  1467.         error.errorCode = error.PHOTOSERVICE_LOGIN_FAILED;
  1468.         break;
  1469.  
  1470.       case "012":
  1471.         error.errorCode = error.PHOTOSERVICE_SESSION_KEY_EXPIRED;
  1472.         break;
  1473.  
  1474.       case "101":
  1475.         error.errorCode = error.PHOTOSERVICE_EMPTY_ALBUMNAME;
  1476.         break;
  1477.  
  1478.       case "102":
  1479.         error.errorCode = error.PHOTOSERVICE_INVALID_USER;
  1480.         break;
  1481.  
  1482.       case "103":
  1483.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1484.         break;
  1485.  
  1486.       case "104":
  1487.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1488.         break;
  1489.  
  1490.       case "105":
  1491.         error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
  1492.         break;
  1493.  
  1494.       case "106":
  1495.         error.errorCode = error.PHOTOSERVICE_INVALID_SEARCH_QUERY;
  1496.         break;
  1497.  
  1498.       case "107":
  1499.         error.errorCode = error.PHOTOSERVICE_INVALID_ALBUM;
  1500.         break;
  1501.  
  1502.       case "108":
  1503.         error.errorCode = error.PHOTOSERVICE_PRIVATE_ALBUM;
  1504.         break;
  1505.  
  1506.       case "109":
  1507.         error.errorCode = error.PHOTOSERVICE_PHOTOS_IN_ALBUM_LIMIT_REACHED;
  1508.         break;
  1509.  
  1510.       case "110":
  1511.         error.errorCode = error.PHOTOSERVICE_NO_FILE_UPLOADED;
  1512.         break;
  1513.  
  1514.       case "111":
  1515.         error.errorCode = error.PHOTOSERVICE_ALBUM_IS_OVER_SIZE_LIMIT;
  1516.         break;
  1517.  
  1518.       case "112":
  1519.         error.errorCode = error.PHOTOSERVICE_INVALID_UPLOAD_FILE;
  1520.         break;
  1521.  
  1522.       case "113":
  1523.         error.errorCode = error.PHOTOSERVICE_DUPLICATE_FILENAME;
  1524.         break;
  1525.  
  1526.       case "115":
  1527.         error.errorCode = error.PHOTOSERVICE_INVALID_ALBUMNAME;
  1528.         break;
  1529.  
  1530.       case "116":
  1531.         error.errorCode = error.PHOTOSERVICE_DUPLICATE_ALBUMNAME;
  1532.         break;
  1533.  
  1534.       case "314":
  1535.         error.errorCode = error.PHOTOSERVICE_FILE_IS_OVER_SIZE_LIMIT;
  1536.         break;
  1537.  
  1538.       default:
  1539.       {
  1540.         error.errorCode = error.PHOTOSERVICE_UNKNOWN_ERROR;
  1541.         if (serverErrorMessage && serverErrorMessage.length) {
  1542.           error.serviceErrorString = serverErrorMessage;
  1543.         }
  1544.       }; break;
  1545.     }
  1546.     return error;
  1547.   }
  1548.   return null;
  1549. }
  1550.  
  1551. photobucketService.prototype.cancelUpload =
  1552. function photobucketService_cancelUpload()
  1553. {
  1554.   try {
  1555.     this.mUploader.req.abort();
  1556.   } catch(e) { }
  1557. }
  1558.  
  1559. photobucketService.prototype.updateActions =
  1560. function photobucketService_updateActions(aURN)
  1561. {
  1562.   var coopObj = this.faves_coop.get(aURN);
  1563.   if (coopObj.isInstanceOf(this.faves_coop.Account)) {
  1564.     coopObj.enabledAction.removeAll();
  1565.     var serviceActions = this.pbService.children.enumerate();
  1566.     while (serviceActions.hasMoreElements()) {
  1567.       var action = serviceActions.getNext();
  1568.       if (action.flavour == "accountaction" ||
  1569.           action.flavour == "accountview" ||
  1570.           action.flavour == "view")
  1571.       {
  1572.         coopObj.enabledAction.add(action);
  1573.       }
  1574.     }
  1575.   }
  1576. }
  1577.  
  1578.  
  1579. // flockIPollingService implementation
  1580.  
  1581. photobucketService.prototype.refresh =
  1582. function photobucketService_refresh(aURN, aListener)
  1583. {
  1584.   var refreshItem = this.faves_coop.get(aURN);
  1585.   var inst = this;
  1586.   if (refreshItem.isInstanceOf(this.faves_coop.Account)) {
  1587.     aListener.onResult();
  1588.     return;
  1589.     // Essentially the importStreams actions
  1590.   }
  1591. }
  1592.  
  1593.  
  1594. photobucketService.prototype.migrateAccount =
  1595. function photobucketService_migrateAccount( aId, aUsername) {
  1596.   this.init();
  1597.  
  1598.   var token = '';
  1599.   try {
  1600.     token = flock_getCharPref(PHOTOBUCKET_TOKEN_PREF);
  1601.   } catch (e) { }
  1602.  
  1603.   this.addAccountById(aId, false, null, aUsername, token);
  1604. }
  1605.  
  1606. // BEGIN flockIWebService interface
  1607. photobucketService.prototype.addAccountById =
  1608. function photobucketService_addAccountById(aAccountID, aIsTransient, aListener, aUsername, aToken)
  1609. {
  1610.   this.logger.debug("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)");
  1611.  
  1612.   if (!aUsername) {
  1613.     // Get the password associated with this account
  1614.     var pw = this.acUtils.getPassword(this.urn+':'+aAccountID);
  1615.     var name = pw.user;
  1616.     var token = '';
  1617.     var pollable = false;
  1618.     var auth = false;
  1619.   } else {
  1620.     var name = aUsername;
  1621.     var token = aToken;
  1622.     var pollable = true;
  1623.     var auth = true;
  1624.   }
  1625.  
  1626.   var accountURN = "urn:flock:photobucket:"+aAccountID;
  1627.   var acctCoopObj = new this.faves_coop.Account(
  1628.     accountURN,
  1629.     {
  1630.       accountId: aAccountID,
  1631.       name: name, // This gets changed to the user's full name once logged in
  1632.       serviceId: PHOTOBUCKET_CONTRACTID,
  1633.       service: this.pbService,
  1634.       URL: "http://www.photobucket.com/",
  1635.       favicon: this.icon,
  1636.       isTransient: aIsTransient,
  1637.       isPollable: pollable,
  1638.       authToken: token,
  1639.       isAuthenticated: auth
  1640.     }
  1641.   );
  1642.   this.faves_coop.accounts_root.children.add(acctCoopObj);
  1643.  
  1644.   var photostreamURN = "urn:flock:stream:photo:photobucket:"+aAccountID;
  1645.   var photostream = new this.faves_coop.Stream(
  1646.     photostreamURN,
  1647.     {
  1648.       name: aAccountID+" Photostream", // This gets changed to full name on login
  1649.       favicon: this.icon,
  1650.       isPollable: true,
  1651.       apiType: "photobucket",
  1652.       userid: aAccountID,
  1653.       flockType: "photostream",
  1654.       URL: 'http://www.photobucket.com/joshuafixme'
  1655.     }
  1656.   );
  1657.   acctCoopObj.children.addOnce(photostream);
  1658.  
  1659.   var notificationsURN = accountURN+":notifications";
  1660.   var notifications = new this.faves_coop.Stream(
  1661.     notificationsURN,
  1662.     {
  1663.       name: "PhotoBucket Notifications",
  1664.       isPollable: false,
  1665.       isIndexable: false,
  1666.       notify: true,
  1667.       serviceId: PHOTOBUCKET_CONTRACTID
  1668.     }
  1669.   );
  1670.   acctCoopObj.children.addOnce(notifications);
  1671.  
  1672.   this.updateActions(accountURN);
  1673.  
  1674.   var mediaFavesURN = "urn:media:favorites";
  1675.   var mediaFaves = this.faves_coop.get(mediaFavesURN);
  1676.   if (!mediaFaves) {
  1677.     mediaFaves = new this.faves_coop.Folder(mediaFavesURN);
  1678.     this.faves_coop.favorites_root.children.add(mediaFaves);
  1679.   }
  1680.  
  1681.   // perhaps we should do a checktoken and if valid go ahead and set the account as logged in?
  1682.  
  1683.   var acct = this.getAccount(acctCoopObj.id());
  1684.   if (aListener) aListener.onSuccess(acct, "addAccount");
  1685.   return acct;
  1686. }
  1687. // END flockIWebService interface
  1688.  
  1689.  
  1690. // BEGIN flockIAuthenticateNewAccount interface
  1691. photobucketService.prototype.authenticateNewAccount =
  1692. function photobucketService_authenticateNewAccount(aListener)
  1693. {
  1694.   this.logger.debug("{flockIAuthenticateNewAccount}.authenticateNewAccount(aListener)");
  1695.   aListener.onStart(null, "newaccountstarted");
  1696.   var inst = this;
  1697.   var tokenListener = {
  1698.     onResult: function (aXML) {
  1699.       inst.logger.debug(".authenticateNewAccount(): tokenListener: onResult()");
  1700.       var params = {
  1701.         service_token: aXML.getElementsByTagName("service_token")[0].firstChild.nodeValue
  1702.       };
  1703.       var sessionListener = {
  1704.         onResult: function (aXML) {
  1705.           inst.logger.debug(".authenticateNewAccount(): tokenListener: sessionListener: onResult()");
  1706.           var username = aXML.getElementsByTagName("username")[0].firstChild.nodeValue;
  1707.           var sessionkey = aXML.getElementsByTagName("session_key")[0].firstChild.nodeValue;
  1708.           inst.mApiUrl = aXML.getElementsByTagName("url")[0].firstChild.nodeValue;
  1709.           inst.getAuthUser().id = username;
  1710.           inst.getAuthUser().username = username;
  1711.           inst.getAuthUser().fullname = username;
  1712.           inst.getAuthUser().sessionkey = sessionkey;
  1713.           inst.state = flockIPhotoAPI.LOGGED_IN;
  1714.           var acctURN = inst.acUtils.getFirstAuthenticatedAccountForService(PHOTOBUCKET_CONTRACTID);
  1715.           var account = null;
  1716.           try {
  1717.             account = inst.getAccount(acctURN);
  1718.             if (!account) throw "ACCOUNT WAS NOT CREATED";
  1719.           } catch (ex) {
  1720.             dump(ex);
  1721.             inst.logger.error(".authenticateNewAccount(): tokenListener: onResult(): ERROR: account was not created");
  1722.             if (aListener) {
  1723.               aListener.onError(null, "newaccount", null);
  1724.             }
  1725.             return;
  1726.           }
  1727.           if (aListener) {
  1728.             aListener.onSuccess(account, "newaccount");
  1729.           }
  1730.         },
  1731.         onError: function (aError) {
  1732.           inst.logger.debug(".authenticateNewAccount(): tokenListener: sessionListener: onError()");
  1733.           inst.logger.debug(">>>>>> " + aError.errorString);
  1734.           inst.state = flockIPhotoAPI.LOGGED_OUT;
  1735.           if (aListener) {
  1736.             aListener.onError(aError, "newaccount", aError);
  1737.           }
  1738.         }
  1739.       };
  1740.       var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  1741.                          .getService(Components.interfaces.nsIWindowMediator);
  1742.       var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  1743.                          .getService(Components.interfaces.nsIWindowWatcher);
  1744.       var topWin = wm.getMostRecentWindow(null);
  1745.       var loginUrl = inst.buildUrl(gStrings["serviceloginURL"], "login", params);
  1746.       var url = "chrome://browser/content/flock/photo/photoLoginWindow.xul?"
  1747.               + "url="+escape(loginUrl)
  1748.               + "&finalString="+escape("You have granted");
  1749.       var chrome = "chrome,close,titlebar,resizable=yes,toolbar,dialog=no,"
  1750.                  + "scrollbars=yes,modal,centerscreen";
  1751.       topWin.open(url, "_blank", chrome);
  1752.       // Wait till the window closes
  1753.       inst.call(sessionListener, "getsession", params2Dictionary(params));
  1754.     },
  1755.     onError: function (aError) {
  1756.       inst.logger.debug(".authenticateNewAccount(): tokenListener: onError()");
  1757.       inst.logger.debug(">>>>>> " + aError.errorString);
  1758.       inst.state = flockIPhotoAPI.LOGGED_OUT;
  1759.       if (aListener) {
  1760.         aListener.onError(aError, "newaccount");
  1761.       }
  1762.     }
  1763.   };
  1764.   if (!this.mAuthUser) {
  1765.     this.mAuthUser = {};
  1766.   }
  1767.   this.getToken(tokenListener);
  1768. }
  1769. // END flockIAuthenticateNewAccount interface
  1770.  
  1771.  
  1772. // BEGIN flockIManageableWebService interface
  1773. photobucketService.prototype.getAccountIDFromDocument =
  1774. function photobucketService_getAccountIDFromDocument(aDocument)
  1775. {
  1776.   this.logger.debug("{flockIManageableWebService}.getAccountIDFromDocument(aDocument)");
  1777.   aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
  1778.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1779.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1780.   if (this.webDetective.detect("photobucket", "accountinfo", aDocument, results)) {
  1781.     return results.getPropertyAsAString("userid");
  1782.   } else {
  1783.     // We could not detect any account info on the login landing page.  This
  1784.     // may mean that it's the API authentication login landing page, which
  1785.     // doesn't have any account info.  If this is the case, then we should
  1786.     // have a temp password entry associated with the session cookie.
  1787.     var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1788.     var pw = this.acUtils.getTempPassword("photobucket:session:"+sessionValue);
  1789.     if (pw) {
  1790.       this.logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  1791.       return pw.user;
  1792.     }
  1793.   }
  1794.   return null;
  1795. }
  1796.  
  1797. photobucketService.prototype.getCredentialsFromForm =
  1798. function photobucketService_getCredentialsFromForm(aForm)
  1799. {
  1800.   var inst = this;
  1801.   var detectForm = function (aType, aResults) {
  1802.     return inst.webDetective.detectForm("photobucket", aType, aForm, aResults);
  1803.   };
  1804.  
  1805.   var formType = "login";
  1806.   var results = getCompTK().newResults();
  1807.   if (!detectForm(formType, results)) {
  1808.     formType = "signup";
  1809.     results = getCompTK().newResults();
  1810.     if (!detectForm(formType, results)) {
  1811.       formType = "changepassword";
  1812.       results = getCompTK().newResults();
  1813.       if (!detectForm(formType, results)) {
  1814.         results = null;
  1815.       }
  1816.     }
  1817.   }
  1818.  
  1819.   if (results) {
  1820.     var pw = {
  1821.       QueryInterface: function(aIID) {
  1822.         if (!aIID.equals(Components.interfaces.nsISupports) &&
  1823.             !aIID.equals(Components.interfaces.nsIPassword) &&
  1824.             !aIID.equals(Components.interfaces.flockIPasswordOrigin)) { 
  1825.           throw Components.interfaces.NS_ERROR_NO_INTERFACE;
  1826.         }
  1827.         return this;
  1828.       },
  1829.       user: results.getPropertyAsAString("username"),
  1830.       password: results.getPropertyAsAString("password"),
  1831.       host: null,
  1832.       formType: formType
  1833.     };
  1834.  
  1835.     // Doing a bit of a hack here...
  1836.     // Since the Photobucket login landing page doesn't always reveal which
  1837.     // account is logged in, we will need to look at the session cookie and
  1838.     // see if its the same as what it was when the user last logged in.  So
  1839.     // at this point we're just storing the account username as a temp
  1840.     // password entry associated with the session cookie token.
  1841.     var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1842.     this.acUtils.clearTempPassword("photobucket:session:"+sessionValue);
  1843.     this.acUtils.setTempPassword("photobucket:session:"+sessionValue, pw.user, "", formType);
  1844.     return pw;
  1845.   }
  1846.   return null;
  1847. }
  1848.  
  1849. photobucketService.prototype.updateAccountStatusFromDocument =
  1850. function photobucketService_updateAccountStatusFromDocument(aDocument)
  1851. {
  1852.   var inst = this;
  1853.   this.logger.debug("{flockIManageableWebService}.updateAccountStatusFromDocument('"+aDocument.URL+"')");
  1854.   if (this.webDetective.detect("photobucket", "loggedout", aDocument, null)) {
  1855.     this.acUtils.markAllAccountsAsLoggedOut(PHOTOBUCKET_CONTRACTID);
  1856.     this.logout();
  1857.   } else if (this.webDetective.detect("photobucket", "loggedin", aDocument, null)) {
  1858.     var acctID;
  1859.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1860.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1861.     if (this.webDetective.detect("photobucket", "accountinfo", aDocument, results)) {
  1862.       acctID = results.getPropertyAsAString("userid");
  1863.     } else {
  1864.       // We could not detect any account info on the login landing page.  This
  1865.       // may mean that it's the API authentication login landing page, which
  1866.       // doesn't have any account info.  If this is the case, then we should
  1867.       // have a temp password entry associated with the session cookie.
  1868.       var sessionValue = this.acUtils.getCookie("http://photobucket.com", "PHPSESSID");
  1869.       var pw = this.acUtils.getTempPassword("photobucket:session:"+sessionValue);
  1870.       if (pw) {
  1871.         this.logger.debug("Sneaky! Got acctID from a temp password associated with session cookie!");
  1872.         acctID = pw.user;
  1873.       }
  1874.     }
  1875.     if (acctID) {
  1876.       var acctURN = this.acUtils.getAccountURNById(this.urn, acctID);
  1877.       if (acctURN) {
  1878.         var acctCoopObj = this.faves_coop.get(acctURN);
  1879.         if (!acctCoopObj.isAuthenticated) {
  1880.           // Re-authenticate the API
  1881.           var reauthListener = {
  1882.             onAuth: function () {
  1883.               inst.logger.debug("reauthListener: onAuth()");
  1884.             },
  1885.             onError: function (aError) {
  1886.               inst.logger.debug("reauthListener: onError()");
  1887.               acctCoopObj.isAuthenticated = false;
  1888.             }
  1889.           };
  1890.           this.getAccount(acctURN).login(reauthListener);
  1891.           // Prematurely set isAuthenticated to TRUE in order to avoid other
  1892.           // asyncronous auth attempts before this one completes.
  1893.           acctCoopObj.isAuthenticated = true;
  1894.         }
  1895.       }
  1896.     }
  1897.   }
  1898. }
  1899. // END flockIManageableWebService interface
  1900.  
  1901.  
  1902. // BEGIN flockIMediaWebService interface
  1903. photobucketService.prototype.decorateForMedia =
  1904. function photobucketService_decorateForMedia(aDocument)
  1905. {
  1906.   this.logger.debug("{flockIMediaWebService}.decorateForMedia(aDocument)");
  1907.   var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1908.                           .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1909.   if (this.webDetective.detect("photobucket", "media", aDocument, results)) {
  1910.     var userid = results.getPropertyAsAString("userid");
  1911.     var mediaArr = new Array();
  1912.  
  1913.     if (!aDocument._flock_decorations) {
  1914.       aDocument._flock_decorations = new Object();
  1915.     }
  1916.     var media = {
  1917.       name: userid,
  1918.       query: 'user:' + userid + "|username:" + userid,
  1919.       label: userid,
  1920.       favicon: this.icon,
  1921.       service: this.shortName
  1922.     }
  1923.     mediaArr.push(media);
  1924.  
  1925.     aDocument._flock_decorations.mediaArr = mediaArr;
  1926.     this.obs.notifyObservers(aDocument, 'media', 'media:update');
  1927.   }
  1928. }
  1929.  
  1930. photobucketService.prototype.handlesMediaStream =
  1931. function photobucketService_handlesMediaStream() 
  1932. {
  1933.   return true;
  1934. }
  1935.  
  1936. photobucketService.prototype.checkIsStreamUrl =
  1937. function photobucketService_checkIsStreamUrl(aUrl)
  1938. {
  1939.   // Videos:
  1940.   // /player.swf?file=http://vid166.photobucket.com/albums/u113/h671226/Smart_Dog.flv
  1941.   if (aUrl.match(/^\/player.swf\?.*\.photobucket\.com\/albums\/[^\/]+\/([^\/]+)\/.+/)) {
  1942.     return true;
  1943.   }
  1944.   
  1945.   // Images:
  1946.   // http://i166.photobucket.com/albums/u105/such-a-craka/thfhg.jpg
  1947.   if (aUrl.match(/^https?:\/\/[^\/]*\.?photobucket\.com\/albums\/[^\/]+\/([^\/]+)\/.+/)) {
  1948.     return true;
  1949.   }
  1950.   return false;
  1951. }
  1952.  
  1953. photobucketService.prototype.getMediaQueryFromURL =
  1954. function photobucketService_getMediaQueryFromURL(aUrl, aListener)
  1955. {
  1956.   var userName = null;
  1957.   var userMatch = aUrl.match(/^https?:\/\/[^\/]*\.?photobucket\.com\/albums\/[^\/]+\/([^\/]+)\/.+/);
  1958.   if (userMatch){
  1959.     userName = userMatch[1];
  1960.   } else {
  1961.     var userMatch = aUrl.match(/^\/player.swf\?.*\.photobucket\.com\/albums\/[^\/]+\/([^\/]+)\/.+/);
  1962.     if (userMatch){
  1963.       userName = userMatch[1];
  1964.     }
  1965.   }
  1966.  
  1967.   if (userName) {
  1968.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  1969.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  1970.     results.setPropertyAsAString("query", "user:" + userName + "|username:" + userName);
  1971.     results.setPropertyAsAString("title", this.title + " user: " + userName);
  1972.     aListener.onSuccess(results, "query");
  1973.   } else {
  1974.     aListener.onError(null, "Unable to get user.", null);
  1975.   }
  1976. }
  1977. // END flockIMediaWebService interface
  1978.  
  1979.  
  1980. photobucketService.prototype.doMigration =
  1981. function photobucketService_doMigration(aDatasource, aAccountURN) {
  1982.   var root = RDFS.GetResource(PEOPLE_PHOTO_RDF_ROOT);
  1983.   var foundFlickrPeople = false;
  1984.   var children = RDFCU.MakeSeq(aDatasource, root).GetElements();
  1985.  
  1986.   while (children && children.hasMoreElements()) {
  1987.     var child = children.getNext();
  1988.     child.QueryInterface(Components.interfaces.nsIRDFResource);
  1989.  
  1990.     var id = aDatasource.GetTarget(child, RDFS.GetResource(FLOCK_NS+"id"), true);
  1991.     id instanceof Components.interfaces.nsIRDFLiteral;
  1992.  
  1993.     var username = aDatasource.GetTarget(child, RDFS.GetResource(FLOCK_NS+"username"), true);
  1994.     username instanceof Components.interfaces.nsIRDFLiteral;
  1995.  
  1996.     var fullname = aDatasource.GetTarget(child, RDFS.GetResource(FLOCK_NS+"fullname"), true);
  1997.     fullname instanceof Components.interfaces.nsIRDFLiteral;
  1998.  
  1999.     var iconUrl = aDatasource.GetTarget(child, RDFS.GetResource(FLOCK_NS+"iconUrl"), true);
  2000.     iconUrl instanceof Components.interfaces.nsIRDFLiteral;
  2001.  
  2002.     var apiShortName = aDatasource.GetTarget(child, RDFS.GetResource(FLOCK_NS+"apiShortName"), true);
  2003.     apiShortName instanceof Components.interfaces.nsIRDFLiteral;
  2004.  
  2005.     if (apiShortName.Value == 'photobucket') {
  2006.       var newContact = Components.classes[FLOCK_PHOTOPERSON_CONTRACTID]
  2007.                                  .createInstance(flockIPhotoPerson);
  2008.       newContact.id = id.Value;
  2009.       newContact.username = username.Value;
  2010.       newContact.fullname = fullname.Value;
  2011.       newContact.avatarUrl = iconUrl.Value;
  2012.  
  2013.       this.mMigratingAccountURN = aAccountURN;
  2014.       var personId = this.addCoopPerson(newContact);
  2015.       this.mMigratingAccountURN = null;
  2016.  
  2017.       // create arc from person to account
  2018.       var newPerson = this.faves_coop.get(personId);
  2019.       var account = this.faves_coop.get(aAccountURN);
  2020.       newPerson.account.addOnce(account);
  2021.     } else if (apiShortName.Value == 'flickr') {
  2022.       foundFlickrPeople = true;
  2023.     }
  2024.   }
  2025. }
  2026.  
  2027.  
  2028. // ================================================
  2029. // ========== BEGIN XPCOM Module support ==========
  2030. // ================================================
  2031.  
  2032. function createModule(aParams) {
  2033.   return {
  2034.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  2035.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  2036.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  2037.                                         aParams.contractID, aFileSpec,
  2038.                                         aLocation, aType );
  2039.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  2040.         .getService(Ci.nsICategoryManager);
  2041.       if (!aParams.categories) { aParams.categories = []; }
  2042.       for (var i = 0; i < aParams.categories.length; i++) {
  2043.         var cat = aParams.categories[i];
  2044.         catMgr.addCategoryEntry( cat.category, cat.entry,
  2045.                                  cat.value, true, true );
  2046.       }
  2047.     },
  2048.     getClassObject: function (aCompMgr, aCID, aIID) {
  2049.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  2050.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  2051.       return { // Factory
  2052.         createInstance: function (aOuter, aIID) {
  2053.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  2054.           var comp = new aParams.componentClass();
  2055.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  2056.           return comp.QueryInterface(aIID);
  2057.         }
  2058.       };
  2059.     },
  2060.     canUnload: function (aCompMgr) { return true; }
  2061.   };
  2062. }
  2063.  
  2064. // NS Module entrypoint
  2065. function NSGetModule(aCompMgr, aFileSpec) {
  2066.   return createModule({
  2067.     componentClass: photobucketService,
  2068.     CID: PHOTOBUCKET_CID,
  2069.     contractID: PHOTOBUCKET_CONTRACTID,
  2070.     componentName: CATEGORY_COMPONENT_NAME,
  2071.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  2072.     categories: [
  2073.       { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: PHOTOBUCKET_CONTRACTID },
  2074.       { category: "flockIPhotoAPI", entry: CATEGORY_ENTRY_NAME, value: PHOTOBUCKET_CONTRACTID },
  2075.       { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: PHOTOBUCKET_CONTRACTID }
  2076.     ]
  2077.   });
  2078. }
  2079.  
  2080. // ========== END XPCOM Module support ==========
  2081.  
  2082.  
  2083.  
  2084. // ====================================================
  2085. // ========== BEGIN photobucketAccount class ==========
  2086. // ====================================================
  2087.  
  2088. function photobucketAccount()
  2089. {
  2090.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  2091.                            .getService(Components.interfaces.flockIAccountUtils);
  2092.   this.service = Components.classes[PHOTOBUCKET_CONTRACTID]
  2093.                            .getService(Components.interfaces.flockIWebService)
  2094.                            .QueryInterface(Components.interfaces.flockIPhotoAPI);
  2095.   this._coop = Components.classes["@flock.com/singleton;1"]
  2096.                          .getService(Components.interfaces.flockISingleton)
  2097.                          .getSingleton("chrome://browser/content/flock/common/load-faves-coop.js")
  2098.                          .wrappedJSObject;
  2099.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  2100.   this.logger.init("photobucket");
  2101.   this._ctk = {
  2102.     interfaces: [
  2103.       "nsISupports",
  2104.       "flockIWebServiceAccount",
  2105.       "flockIMediaWebServiceAccount",
  2106.       "flockIMediaUploadAccount"
  2107.     ]
  2108.   };
  2109.   getCompTK().addAllInterfaces(this);
  2110. }
  2111.  
  2112.  
  2113. // BEGIN flockIWebServiceAccount interface
  2114. photobucketAccount.prototype.login =
  2115. function photobucketAccount_login(aListener)
  2116. {
  2117.   this.logger.debug("{flockIWebServiceAccount}.login()");
  2118.   this.service.login(this.urn, aListener);
  2119. }
  2120.  
  2121. photobucketAccount.prototype.activate =
  2122. function photobucketAccount_activate(aListener)
  2123. {
  2124.   this.logger.debug("{flockIWebServiceAccount}.activate()");
  2125.   var acctCoopObj = this._coop.get(this.urn);
  2126.   var inst = this;
  2127.   var listener = {
  2128.     onAuth: function() {
  2129.       inst.logger.debug("activate() listener onAuth()");
  2130.       acctCoopObj.isPollable = true;
  2131.       aListener.onSuccess(inst, "accountAuthorized");
  2132.       try {
  2133.         var authUser = inst.getAuthUser();
  2134.         acctCoopObj.name = authUser.fullname;
  2135.         var photostreamURN = "urn:flock:stream:photo:photobucket:"+acctCoopObj.accountId;
  2136.         var photostream = inst._coop.get(photostreamURN);
  2137.         photostream.name = authUser.fullname+" Photostream";
  2138.       } catch (ex) {
  2139.         inst.logger.error(ex + '::' + ex.lineNumber);
  2140.       }
  2141.     },
  2142.     onError: function(aError) {
  2143.       inst.logger.debug("activate() listener onError()");
  2144.       try {
  2145.         var authUser = inst.getAuthUser();
  2146.         var acctCoopObj = inst._coop.get(inst.urn);
  2147.         acctCoopObj.isAuthenticated = false;
  2148.       } catch (ex) {
  2149.         // Don't really need to do anything, we were just trying to be nice
  2150.         // anyway...
  2151.       }
  2152.     }
  2153.   }
  2154.   this.service.login(this.urn, listener);
  2155.   // Pre-emptively setting isAuthenticated to TRUE in order to avoid further
  2156.   // auth attemps while this one is completing.
  2157.   acctCoopObj.isAuthenticated = true;
  2158. }
  2159.  
  2160. photobucketAccount.prototype.deactivate =
  2161. function photobucketAccount_deactivate()
  2162. {
  2163.   this.logger.debug("{flockIWebServiceAccount}.deactivate()");
  2164.   var acctCoopObj = this._coop.get(this.urn);
  2165.   acctCoopObj.isPollable = false;
  2166.   acctCoopObj.isAuthenticated = false;
  2167. }
  2168.  
  2169. photobucketAccount.prototype.keep =
  2170. function photobucketAccount_keep()
  2171. {
  2172.   var c_acct = this._coop.get(this.urn);
  2173.   c_acct.isTransient = false;
  2174. }
  2175. // END flockIWebServiceAccount interface
  2176.  
  2177. // ========== END photobucketAccount class ==========
  2178.